home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #14 / Monster Media No. 14 (April 1996) (Monster Media, Inc.).ISO / prog_pas / tsfaqp29.zip / FAQPAS2.TXT < prev    next >
Internet Message Format  |  1996-01-01  |  61KB

  1. From ts@uwasa.fi Mon Jan 1 00:00:00 1996
  2. Subject: FAQPAS2.TXT contents
  3.  
  4.                              Copyright (c) 1993-1996 by Timo Salmi
  5.                                                All rights reserved
  6.  
  7. FAQPAS2.TXT More frequently (and not so frequently) asked Turbo
  8. Pascal questions with Timo's answers. The items are in no particular
  9. order.
  10.  
  11. You are free to quote brief passages from this file provided you
  12. clearly indicate the source with a proper acknowledgment.
  13.  
  14. Comments and corrections are solicited. But if you wish to have
  15. individual Turbo Pascal consultation, please post your questions to
  16. a suitable Usenet newsgroup like news:comp.lang.pascal.borland. It
  17. is much more efficient than asking me by email. I'd like to help,
  18. but I am very pressed for time. I prefer to pick the questions I
  19. answer from the Usenet news. Thus I can answer publicly at one go if
  20. I happen to have an answer. Besides, newsgroups have a number of
  21. readers who might know a better or an alternative answer. Don't be
  22. discouraged, though, if you get a reply like this from me. I am
  23. always glad to hear from fellow Turbo Pascal users.
  24.  
  25. ....................................................................
  26. Prof. Timo Salmi   Co-moderator of news:comp.archives.msdos.announce
  27. Moderating at ftp:// & http://garbo.uwasa.fi archives  193.166.120.5
  28. Department of Accounting and Business Finance  ; University of Vaasa
  29. ts@uwasa.fi http://uwasa.fi/~ts BBS 961-3170972; FIN-65101,  Finland
  30.  
  31. --------------------------------------------------------------------
  32. 31) How does one store, and then restore the original screen?
  33. 32) How can I convert a TPU unit of one TP version to another?
  34. 33) Which error is e.g. Runtime error 205, etc
  35. 34) Why can't I open read-only files? I get "File access denied".
  36. 35) How do I obtain high and low parts of a byte variable?
  37. 36) How can I set a hi-intensity color background in the text mode?
  38. 37) Where can I find a program to convert (Turbo) Pascal to C?
  39. 38) How can I read input without echoing to the screen?
  40. 39) How can I edit the readln input stream?
  41. 40) How can I write (brand) something into my executables?
  42. 41) What is wrong with my program? It hangs without a clear pattern?
  43. 42) How do I convert a decimal word into a hexadecimal string, etc?
  44. 43) How to determine the last drive?
  45. 44) How can I put a running clock into my Turbo Pascal program?
  46. 45) How to establish if a name refers to a directory or not?
  47. 46) How does one disable alt-ctrl-del?
  48. 47) How can I test whether a file exists?
  49. 48) What is the name of the current Turbo Pascal program?
  50. 49) How is the code for rebooting the PC written in Turbo Pascal?
  51. 50) How can I write inline code?
  52. 51) I am running out of memory when compiling my large program.
  53. 52) How do I avoid scrolling in the last column of the last row?
  54. 53) How can one hide (or unhide) a directory using a TP program?
  55. 54) How do I test whether a file is already open in a TP program?
  56. 55) How can I test and convert a numerical string into a real?
  57. 56) How can I reverse a TP .EXE or .TPU back into source code?
  58. 57) How can I calculate the difference between two points of time?
  59. 58) Is a program running stand-alone or from within the IDE?
  60. 59) Please explain Turbo Pascal memory addressing to me.
  61. 60) How do I obtain a bit or bits from a byte, a word or a longint?
  62. --------------------------------------------------------------------
  63.  
  64. From ts@uwasa.fi Mon Jan 1 00:00:31 1996
  65. Subject: Saving the screen
  66.  
  67. 31. *****
  68.  Q: How does one store, and then restore the original screen?
  69.  
  70.  A: Here is a simple outline for storing and restoring a text mode
  71. screen in the standard 80 x 25 mode. Note that the code below is
  72. incomplete in a sense that it works for a color monitor only,
  73. because the monochrome screen address is $B000:$0000.
  74.    For storing and restoring the graphics screen see Ohlsen & Stoker
  75. (1989), Turbo Pascal Advanced Techniques, Que, pp 333-337.
  76.   uses Crt;
  77.   type ScreenType = array [1..4000] of byte;        (* 2 x 80 x 25 *)
  78.   var ColorScreen : ScreenType Absolute $B800:$0000;
  79.       SavedScreen : ScreenType;
  80.       posx, posy : byte;
  81.   begin
  82.     SavedScreen := ColorScreen;      (* Save the screen *)
  83.     posx := WhereX; posy := WhereY;  (* Save the cursor position *)
  84.     writeln ('A simple demo storing and restoring the color text screen');
  85.     writeln ('By Prof. Timo Salmi, ts@uwasa.fi');
  86.     writeln; write ('Press <-'''); readln;
  87.     ColorScreen := SavedScreen;   (* Restore the screen *)
  88.     GotoXY(posx,posy);            (* Go to the stored cursor position *)
  89.   end.
  90. If you would prefer not using the Crt unit, you can apply WHEREXFN,
  91. WHEREYFN, and GOATXY from TSUNTG.TPU from my units collection
  92. ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
  93.    If you wish to test for the monitor type, that is choose between
  94. $B800:$0000 and $B000:$0000 bases, you can use the following
  95. function to test for the monochrome adapter.
  96.   function MONOFN : boolean;
  97.   var regs : registers;
  98.   begin
  99.     FillChar (regs, SizeOf(regs), 0);
  100.     regs.ah := $0F;
  101.     Intr ($10, regs);
  102.     monofn := (regs.al = 7);
  103.   end;  (* monofn *)
  104. --------------------------------------------------------------------
  105.  
  106. From ts@uwasa.fi Mon Jan 1 00:00:32 1996
  107. Subject: Converting TPUs
  108.  
  109. 32. *****
  110.  Q: How can I convert a TPU unit of one TP version to another?
  111.  
  112.  A: Forget it. In practical terms such a conversion is not on. The
  113. Turbo Pascal TPU units are strictly version dependent. If there were
  114. a working solution I assume we would have heard of it long since.
  115. The hacks that have been tried won't solve this dilemma. For all
  116. practical purposes you need the source code and the relevant
  117. compiler version.
  118.    You may nevertheless wish to ascertain for which version a TPU
  119. unit has been compiled. This is very simple. Just look at the first
  120. four character of a TPU file. The codes are
  121.  TPU0  for 4.0
  122.  TPU5  for 5.0
  123.  TPU6  for 5.5
  124.  TPU9  for 6.0
  125.  TPUQ  for 7.0 real mode
  126. But don't go editing these. It will not get you anywhere.
  127. --------------------------------------------------------------------
  128.  
  129. From ts@uwasa.fi Mon Jan 1 00:00:33 1996
  130. Subject: Finding about runtime errors
  131.  
  132. 33. *****
  133.  Q: Which error is e.g. Runtime error 205
  134.  
  135.  A: Basically this is a case of RTFM (read the f*ing manual). But it
  136. is very easy to find out even without resorting to the manual. Put
  137. temporarily the statement RunError (205); as the first statement of
  138. your program. Then run your program from the Turbo Pascal IDE, that
  139. is from within the TP editor. The description of the error will
  140. appear.
  141.    If you run a program from within a Turbo Pascal IDE, it is
  142. advisable to turn on the debug options on. You'll get both the error
  143. number and the description. Furthermore by pressing F1 after the
  144. error you get its description in a more verbal format.
  145.    One further trick is to put "uses TSERR"; (Include verbal
  146. run-time error messages) into your program. If you do that, the
  147. run-time errors will be given with a verbal description not just as
  148. a number. TSERR.TPU is part of my TPU collection at Garbo
  149. ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
  150.    In TP 7.0 the run time errors can also be found by invoking
  151. "Help" from the main manu (Alt-H) and selecting "Error messages".
  152. --------------------------------------------------------------------
  153.  
  154. From ts@uwasa.fi Mon Jan 1 00:00:34 1996
  155. Subject: Opening read-only files
  156.  
  157. 34. *****
  158.  Q: Why can't I open read-only files? I get "File access denied".
  159.  
  160.  A: The answer is rather simple, but it is not well displayed in the
  161. manuals. In order to read a read-only file you have to set the
  162. FileMode as 0 like below. Else you'll get runtime error 005 "File
  163. access denied".
  164.   var f      : text;          (* Can be any file type *)
  165.       savefm : byte;
  166.   begin
  167.     savefm := FileMode;       (* Save the current FileMode status *)
  168.     FileMode := 0;            (* The default is 2 *)
  169.     assign (f, 'readonly.txt');
  170.     reset (f);
  171.     { have your wicked ways }
  172.     close (f);
  173.     FileMode := savefm;       (* Restore the original FileMode *)
  174.   end.
  175. --------------------------------------------------------------------
  176.  
  177. From ts@uwasa.fi Mon Jan 1 00:00:35 1996
  178. Subject: Getting a nybble from a byte
  179.  
  180. 35. *****
  181.  Q: I have a variable of type BYTE and would like to extract two
  182. numbers from it. (The first 4 bits making up number A, the second 4
  183. bits making up number B).  How can I extract these two numbers?
  184.  
  185.  A: Ah, this question brings back the good bad old days of the
  186. Commodore C64 programming when bit operations were rather a rule
  187. than an exception. Here is the solution.
  188.   function HIBYTEFN (x : byte) : byte;
  189.   begin
  190.     hibytefn := x Shr 4;           (* Shift right by four bits *)
  191.   end;
  192.   {}
  193.   function LOBYTEFN (x : byte) : byte;
  194.   begin
  195.     lobytefn := x and 15;          (* x and 00001111 *)
  196.   end;
  197. From Patrick Taylor (exuptr@exu.ericsson.se): Ah, leave it to Timo
  198. to come up with a different way! An other is (n div 16)
  199. (n mod 16).
  200.    Patrick is right.  But unless the compiler is optimized, the
  201. former produces more efficient code. Not that it really makes any
  202. practical difference whatsoever.
  203.    Of course the fastest code is produced using assembler as pointed
  204. out by Maarten Pennings (maarten@cs.ruu.nl) who provided the
  205. following inline example:
  206.   function high(b:byte):byte;
  207.     inline($58         { POP AX      | AH=?, AL=b       }
  208.           /$30/$e4     { XOR AH,AH   | AH=0, AL=b       }
  209.           /$b9/$04/$00 { MOV CX,0004 | AH=0, AL=b, CL=4 }
  210.           /$d3/$e8     { SHR AX,CL   | AX=b shr 4       }
  211.           );
  212.  
  213.  A2: Getting a word from a longint can alternatively be achieved
  214. without any calculations by using a kind of typecasting. Below is
  215. the code I have utilized in ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
  216.   (* Get the high-order word of the longint argument *)
  217.   function HIWORDFN (x : longint) : word;
  218.   type type1 = record
  219.                  low  : word;
  220.                  high : word;
  221.                end;
  222.   var m1 : type1 absolute x;
  223.   begin
  224.     hiwordfn := m1.high;
  225.   end;  (* hiwordfn *)
  226. --------------------------------------------------------------------
  227.  
  228. From ts@uwasa.fi Mon Jan 1 00:00:36 1996
  229. Subject: Setting hi-intensity background
  230.  
  231. 36. *****
  232.  Q: How can I set a hi-intensity color background in the text mode?
  233.  
  234.  A: As you should know, the you can test for a blinking text for
  235. example as follows.
  236.   uses Crt;
  237.   begin
  238.     TextColor (11 + 128);  (* or LightCyan + Blink *)
  239.     TextBackground (Blue);
  240.     writeln ('What''s the catch?');  (* An aside, note the '' pair *)
  241.   end.
  242. In the above, bit 7 (the 128) controls the blinking. If you have at
  243. least an EGA, you can alter the interpretation of the highest text
  244. color bit to denote a hi-intensity background, but then you lose the
  245. the blinking. The following piece of code disables blinking,
  246. enabling a hi-intensity background.
  247.   uses Dos;
  248.   var regs : registers;
  249.   begin
  250.     FillChar (regs, SizeOf(regs), 0); (* An initialization precaution *)
  251.     regs.ah := $10;                   (* Function $10 *)
  252.     regs.al := $03;                   (* Subfunction $03 *)
  253.     regs.bl := $00;
  254.     Intr ($10, regs);      (* ROM BIOS video driver interrupt *)
  255.   end.
  256. To enable blinking again, set regs.bl := $01; Any high-intensity
  257. background you may have currently on the screen, will instantly
  258. change into a blinking text a a low-intensity background.
  259.  
  260.  A2: The previous answer assumes at least an EGA. Otherwise ports
  261. must be accessed. This is both advanced and dangerous programming,
  262. because errors in handling posts can do real harm. Besides it is
  263. fair to require at least an EGA in writing modern programs, at least
  264. for non-laptops, and on the latter the colors don't really matter
  265. for CGA and below. Let's take a look, nevertheless, how this is done
  266. for a CGA. Note that this won't work an an EGA and beyond, not at
  267. least in my tests. For detecting the video adapter you have, see the
  268. DetectGraph procedure in you Turbo Pascal manual.
  269.    First we need some basics from MEMORY.LST in Ralf Brown's
  270. ftp://garbo.uwasa.fi/pc/programming/inter48b.zip (or whatever
  271. version is current):
  272.  Format of BIOS Data Segment at segment 40h:
  273.   63h WORD Video CRT controller base address: color=03D4h, mono=03B4h
  274.   65h BYTE Video current setting of mode select register 03D8h/03B8h
  275. From David Jurgens's ftp://garbo.uwasa.fi/pc/programming/helppc21.zip
  276. we see
  277.   3D0-3DF Color Graphics Monitor Adapter (ports 3D0-3DB are
  278.           write only, see 6845)
  279.   3D8 6845 Mode control register (CGA, EGA, VGA, except PCjr)
  280. From Darryl Friesen's (friesend@jester.usask.ca) in the late
  281. comp.lang.pascal we have, the following procedure, with my own added
  282. comments (* *).
  283.   procedure SetBlinkState (state : boolean);
  284.   var ModeRegPort : word;
  285.       ModeReg     : byte;
  286.   begin
  287.     Inline($FA); { CLI }           (* Interrupts off *)
  288.     ModeRegPort := MemW[$0040:$0063]+4;  (* Typically $03D4+4 = $03D8 *)
  289.     ModeReg := Mem[$0040:$0065];   (* Typically 1001 *)
  290.     if state then                  (* Bit 5 controls blink enable *)
  291.       ModeReg := ModeReg or $20    (* $20 = 00100000 (base2) *)
  292.     else
  293.       ModeReg := ModeReg and $DF;  (* $DF = 11011111 disable *)
  294.     Port[ModeRegPort] := ModeReg;  (* Typically $9 = 00001001 *)
  295.     Mem[$0040:$0065] := ModeReg;   (*       or $29 = 00101001 *)
  296.     Inline($FB) { STI }            (* Interrupts on *)
  297.   end;
  298. --------------------------------------------------------------------
  299.  
  300. From ts@uwasa.fi Mon Jan 1 00:00:37 1996
  301. Subject: Pascal to C
  302.  
  303. 37. *****
  304.  Q: Where can I find a program to convert (Turbo) Pascal to C?
  305.  
  306.  A: This is a relevant question, but I have placed elsewhere the
  307. tips on the "looking for a program" questions. Here are the
  308. pointers to further pointers :-). (The FAQ versions might have been
  309. updated since I wrote this.)
  310.  ftp://garbo.uwasa.fi/pc/pd2/camfaq.zip
  311.  camfaq.zip comp.archives.msdos.(d/announce) FAQ (general finding)
  312.  :
  313.  ftp://garbo.uwasa.fi/pc/ts/tsfaqn43.zip
  314.  tsfaqn43.zip Questions from UseNet and Timo's answers
  315.  :
  316.  ftp://garbo.uwasa.fi/pc/pd2/faquote.zip
  317.  faquote.zip Old information from tsfaq Frequently Asked Questions
  318. --------------------------------------------------------------------
  319.  
  320. From ts@uwasa.fi Mon Jan 1 00:00:38 1996
  321. Subject: Turning off the input echo
  322.  
  323. 38. *****
  324.  Q: How can I read input without echoing to the screen?
  325.  
  326.  A: It is fairly simple. Study this example source code, with the
  327. manual, if need be.
  328.   uses Crt;
  329.   var password : string;
  330.   {}
  331.   (* Read without echoing *)
  332.   procedure GETPASS (var s : string);
  333.   var key : integer;
  334.       ch : char;
  335.   begin
  336.     s := '';
  337.     repeat
  338.       ch := ReadKey; key := ord (ch);
  339.       case key of
  340.          0 : ch := ReadKey;  (* Discard two-character keys, like F1 *)
  341.         13 : exit;           (* Enter has been pressed *)
  342.         1..12,13..31,255 :;  (* Discard the special characters *)
  343.         else s := s + ch;
  344.       end;
  345.    until false;
  346.   end;  (* getpass *)
  347.   {}
  348.   (* The main program *)
  349.   begin
  350.     write ('Password: ');
  351.     GETPASS (password);
  352.     writeln;
  353.     writeln (password);
  354.   end.
  355.   {}
  356. If you wish to be able to edit the input stream, like having the
  357. BackSpace functional, that is more complicated, and is left as an
  358. exercise after these basics. A hint: 8 : Delete (s, Length(s), 1);
  359.    There is another approach to this problem pointed out by Colin
  360. Lamond colin@sound.demon.co.uk. Quite innovative in its simplicity
  361. once one comes to think of it. "Set the textcolor, and the
  362. textbackground to the same color, and so the typed text can not be
  363. seen on the screen."
  364. --------------------------------------------------------------------
  365.  
  366. From ts@uwasa.fi Mon Jan 1 00:00:39 1996
  367. Subject: Input line-editing
  368.  
  369. 39. *****
  370.  Q: How can I edit the readln input stream?
  371.  
  372.  A: In practice, if you wish to use anything beyond simple the
  373. BackSpace deleting, you'll have to build your own line editing
  374. routines expanding on the code in the previous item. It is quite a
  375. task, and you can alternatively find the preprogrammed routines in
  376. my Turbo Pascal units ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip (or
  377. whatever version number is current).
  378.  EDRDEBLN Editable Readln with ctrl-c, break trapping, pre-fill etc
  379.  EDRDEFLN Editable Readln with recall, pre-fill, and insert toggle
  380.  EDRDLN   Readln with line-editing potential (the simplest)
  381.  EDREABLN Edreadln with ctrl-c and break trapping
  382.  EDREADLN Editable Readln with recall, and insert toggle
  383. --------------------------------------------------------------------
  384.  
  385. From ts@uwasa.fi Mon Jan 1 00:00:40 1996
  386. Subject: Executable branding
  387.  
  388. 40. *****
  389.  Q: How can I write (brand) something into my executables?
  390.     Here is the actual question that led me to writing this item: 'I
  391.     am very interested in the .EXE "branding" techniques you use in
  392.     your TSUNTI unit. Would it be possible to get hold of the source
  393.     code for that unit, as it would save me from having to re-invent
  394.     the wheel?'
  395.  
  396.  A: What you are referring to is
  397.  BRANDEXE Store information within your program's .exe file (MS-DOS 3.0+)
  398.  CHKSUMFN Checksum self-test to detect any tampering (MS-DOS 3.0+)
  399.  USECOUNT Get the number of times the program has been used
  400. Sorry no, I don't want to distribute my source codes from
  401. ftp://garbo.uwasa.fi/pc/turbopas/ts/tspa3470.zip. Besides they would
  402. be less useful to you than you may think because internally my
  403. programs are in Finnish, comments, variable and procedure names, and
  404. all. But I can hopefully help you by giving a reference to a similar
  405. code.  Please see Ohlsen & Stoker, Turbo Pascal Advanced Techniques,
  406. Que, 1989, p. 420.
  407. --------------------------------------------------------------------
  408.  
  409. From ts@uwasa.fi Mon Jan 1 00:00:41 1996
  410. Subject: Elusive, inconsistent errors
  411.  
  412. 41. *****
  413.  Q: What is wrong with my program? It hangs without a clear pattern?
  414.  
  415.  A: With experience one learns that some programming errors are very
  416. elusive. I have many times seen users declaring that they have found
  417. a bug in Turbo Pascal, but in the overwhelming majority of cases it
  418. still is just a programming error, which just is more difficult to
  419. find than the more clear-cut cases. When you have symptoms like your
  420. program crashing from within the IDE, but working seemingly all
  421. right when called as stand-alone, or something equally strange, you
  422. might have one of the following problems.
  423. - A variable or some variables in your code are uninitialized thus
  424.   getting random values, which differ depending on your environment.
  425. - Your indexes are overflowing. Set on the range check {$R+}
  426.   directive for testing.
  427. - An error in the pointer logic.
  428. Normal debugging does not necessarily help in locating these errors
  429. because one is easily led to debugging the wrong parts of one's
  430. program. Especially the latter two reasons can cause errors which
  431. seemingly have nothing to do with the actual cause. This results
  432. from the fact that indexing and pointer errors can overwrite parts
  433. of memory causing strange quirks in your program. If you have used
  434. indexing with {$R-} or if you use pointer operations, sooner or
  435. later you are bound to have these problems in developing your
  436. applications.
  437.    See Edward Mitchell (1993), Borland Pascal Developer's Guide,
  438. 275-288 for common programming errors and especially the information
  439. on memory clobbering, in a useful chapter on debugging Turbo Pascal
  440. programs. You might also take a look at your Turbo Pascal User's
  441. Guide. At least version 7.0 has an instructive general
  442. categorization of errors on pages 76-77.
  443. --------------------------------------------------------------------
  444.  
  445. From ts@uwasa.fi Mon Jan 1 00:00:42 1996
  446. Subject: Converting the number base
  447.  
  448. 42. *****
  449.  Q: How do I convert a decimal word into a hexadecimal string, etc?
  450.  
  451.  A: Here is one possibility
  452.   function HEXFN (decimal : word) : string;
  453.   const hexDigit : array [0..15] of char = '0123456789ABCDEF';
  454.   begin
  455.     hexfn := hexDigit[(decimal shr 12)]
  456.           + hexDigit[(decimal shr 8) and $0F]
  457.           + hexDigit[(decimal shr 4) and $0F]
  458.           + hexDigit[(decimal and $0F)];
  459.   end;  (* hexfn *)
  460. Here is another conversion example (from longint to binary string)
  461.   function LBINFN (decimal : longint) : string;
  462.   const BinDigit : array [0..1] of char = '01';
  463.   var i     : byte;
  464.       binar : string;
  465.   begin
  466.     FillChar (binar, SizeOf(binar), ' ');
  467.     binar[0] := chr(32);
  468.     for i := 0 to 31 do
  469.       binar[32-i] := BinDigit[(decimal shr i) and 1];
  470.     lbinfn := binar;
  471.   end;  (* lbinfn *)
  472. For a full set of conversions, both from and to decimal, apply
  473. TSUTNTB.TPU from ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
  474. --------------------------------------------------------------------
  475.  
  476. From ts@uwasa.fi Mon Jan 1 00:00:43 1996
  477. Subject: Identifying the last drive
  478.  
  479. 43. *****
  480.  Q: How to determine the last drive?
  481.  
  482.  A: One way of doing that is utilizing the information in DPB, that
  483. is the Drive Parameter Block, but that is rather complicated, so you
  484. can find that without source code in the TSUNTH unit in
  485. ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip .
  486.  Another way is using interrupt 21H, function 36H to detect if a
  487. drive exists starting from the first drive letter. The code is given
  488. below. The disadvantage of this method is that it does not
  489. distinguish between real and substituted drives.
  490.   uses Dos;
  491.   function LASTDFN : char;  (* Detect last harddisk letter *)
  492.   var regs : registers;
  493.       i    : byte;
  494.   begin
  495.     i := 2;
  496.     repeat
  497.       Inc(i);
  498.       FillChar (regs, SizeOf(regs), 0);
  499.       regs.ah := $36;
  500.       regs.dl := i;
  501.       MsDos(regs);
  502.     until (regs.ax = $FFFF);
  503.     lastdfn := chr(i+63);
  504.   end;  (* lastdfn *)
  505. --------------------------------------------------------------------
  506.  
  507. From ts@uwasa.fi Mon Jan 1 00:00:44 1996
  508. Subject: Clock display in a TP program
  509.  
  510. 44. *****
  511.  Q: How can I put a running clock into my Turbo Pascal program?
  512.  
  513.  A: We are not speaking of a stand-alone TSR-clock (which is a
  514. different task), but considering a clock that continuously displays
  515. the time in some part of the output screen of your Turbo Pascal
  516. program.
  517.     You might first want to read the earlier items about ReadKey
  518. usages if you are not familiar with it (you probably are, because
  519. you would not pose this advanced question if you were a novice). The
  520. items are the unlikely "How do I disable or capture the break key in
  521. Turbo Pascal?" and "How can I read input without echoing to the
  522. screen?"
  523.    The general idea is to make the body of the program a repeat
  524. until loop using ReadKey for input and updating the clock display
  525. at suitable junctions within the loop. The scheme is thus something
  526. like the following.
  527.   procedure showtime;
  528.     begin
  529.       { if the second has changed, write the time }
  530.     end;
  531.   :
  532.   repeat
  533.     { do whatever }
  534.     showtime;
  535.     if KeyPressed then
  536.       case ReadKey of
  537.         { whatever }
  538.         { exit rules }
  539.       end;
  540.     showtime;
  541.     :
  542.     showtime;
  543.   until false;
  544.    One trick of the trade is that you must not update your clock
  545. each time the clock routine is encountered. You should test if the
  546. second has changed, and update only then. Else you are liable to get
  547. an annoying flicker in your clock.
  548. --------------------------------------------------------------------
  549.  
  550. From ts@uwasa.fi Mon Jan 1 00:00:45 1996
  551. Subject: Is a name a directory
  552.  
  553. 45. *****
  554.  Q: How to establish if a name refers to a directory or not?
  555.  
  556.  A: This question has turned out a bit more complicated than I first
  557. thought. There are several methods, each with some catch. The first
  558. is trying to open the name as a file and observing the IOResult. The
  559. ISDIRFN function in ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip TPU unit
  560. TSUNTJ.TPU is based on this method. Unfortunately it is not always
  561. stable. I have been reported problems in connection with DRDOS by
  562. Richard Breuer (ricki@pool.informatik.rwth-aachen.de) who has
  563. tested these routines.
  564.   The second method (ISDIR2FN) is based on the fact that the file
  565. NUL exists in a directory if the directory exists.
  566.   The thrid method (ISDIR3FN) is a brute force method. It is given
  567. below, since it is quite an instructive little exercise of Turbo
  568. Pascal programming.
  569.   (* Search recursively through a drive's directories.
  570.      Auxiliary, recursive procedure for ISDIR3FN *)
  571.   procedure SEARCHDR (Path, FileSpec : string;
  572.                       name           : string;
  573.                       var found      : boolean);
  574.   var FileInfo : SearchRec;
  575.   begin
  576.     FindFirst (Path + '*.*', Directory, FileInfo);
  577.     while DosError = 0 do
  578.       begin
  579.         if ((FileInfo.Attr and Directory) > 0) and
  580.             (FileInfo.Name <> '.') and
  581.             (FileInfo.Name <> '..') then
  582.               begin
  583.                 SEARCHDR (Path + FileInfo.Name + '\',
  584.                           FileSpec,
  585.                           name,
  586.                           found);
  587.                 if Path + FileInfo.Name + '\' = name then
  588.                   found := true;
  589.               end;
  590.         FindNext (FileInfo);
  591.       end; {while}
  592.   end;  (* searchdr *)
  593.  
  594.   (* Does a name refer to a directory *)
  595.   function ISDIR3FN (name : string) : boolean;
  596.   var drive : char;
  597.       found : boolean;
  598.   begin
  599.     {... Default value ...}
  600.     isdir3fn := false;
  601.     {... Discard empty names ...}
  602.     if name = '' then exit;
  603.     {... Expand into a fully qualified name, makes it uppercase ...}
  604.     name := FExpand (name);
  605.     if name[Length(name)] <> '\' then name := name + '\';
  606.     {... Extract the drive letter from the name ...}
  607.     drive := UpCase (name[1]);
  608.     {... Check first for the root ...}
  609.     if drive + ':\' = name then
  610.       begin isdir3fn := true; exit; end;
  611.     {... Check the rest of the directories recursively ...}
  612.     found := false;
  613.     SEARCHDR (drive + ':\', '*.*', name, found);
  614.     isdir3fn := found;
  615.   end;  (* isdir3fn *)
  616.  
  617. -Date: Mon, 13 Jun 1994 00:13:05 +0000 (GMT)
  618. -From: JEROEN SCHIPPER <JSCHIPPER@HUT.NL>
  619. -To: ts@uwasa.fi (Timo Salmi)
  620. -Subject: Is a name a directory in TP
  621.  
  622. The method I use is simply checking the attribute bit, as this small
  623. program will demonstrate:
  624.   program isdir;
  625.   uses dos;
  626.   var s:string;
  627.       attr:word;
  628.       f:file;
  629.   begin
  630.     repeat
  631.       readln(s);
  632.       if s = '' then break;
  633.       assign(f,s);
  634.       getfattr(f,attr);
  635.       if doserror <> 0 then
  636.         writeln('DOS error code = ', doserror)
  637.       else
  638.       begin
  639.         if attr and directory <> 0 then
  640.           writeln(S,' is a directory')
  641.         else
  642.           writeln(S,' is a not directory')
  643.       end;
  644.     until false;
  645.   end.
  646. The methods you mention in your faq are far more complicated, but
  647. why? Is there are catch why the method above won't work? I guess
  648. don't really understand the problem here.
  649. Jeroen.
  650.  
  651.  A2: This has turned out to be a tricky FAQ. There are some
  652. additional suggestions and comments from the gentle readers. You can
  653. track them from ftp://garbo.uwasa.fi/pc/ts/tspost00.zip index.
  654. --------------------------------------------------------------------
  655.  
  656. From ts@uwasa.fi Mon Jan 1 00:00:46 1996
  657. Subject: Disabling alt-ctrl-del
  658.  
  659. 46. *****
  660.  Q: How does one disable alt-ctrl-del?
  661.  
  662.  A: I can only give a pointer to source code. Take a look at
  663.  4067 Jul 1 1993 ftp://garbo.uwasa.fi/pc/turbopa7/cadthf10.zip
  664.  cadthf10.zip CadThief TP6+ unit for trapping ctrl+alt+del, M.Hanninen
  665. and
  666.  30673 Oct 13 1987 ftp://garbo.uwasa.fi/pc/turbopas/keyint.zip
  667.  keyint.zip Disable alt-ctrl-del + other int09h TP tricks, N.Rubenking
  668. and
  669.  7105 Apr 19 10:51 ftp://garbo.uwasa.fi/pc/turbopa7/cad_int9.zip
  670.  cad_int9.zip Disable Ctrl-Alt-Del via new TP kb interrupt, J.Robertson
  671. Also see Lou Duchez's source code in TSR.SWG examples in the fine
  672. SWAG (SourceWare Archival Group's) collection of TP sources.
  673. Available from the /pc/turbopas directory at Garbo. For the current
  674. references to the SWAG files see ftp://garbo.uwasa.fi/pc/INDEX.ZIP.
  675.    I have utilized alt-ctrl-del disabling at least in one of my own
  676. programs (PESTIKID.EXE). The code is not available, but the general
  677. idea is replacing the old keyboard interrupt ($09) with a handler of
  678. one's own. If the handler detects alt-ctrl-del, the keyboard is
  679. reset, else the handler is chained back to the original interrupt.
  680. The chaining requires a rather complicated inline procedure provided
  681. in TurboPower Software's kit. An additional complication is that the
  682. del keypress must be intercepted already at the relevant port $60,
  683. and the alt and ctrl status must be tested, so that the rebooting
  684. will not be invoked. Resetting the keyboard requires accessing the
  685. $20 and $61 ports.
  686. --------------------------------------------------------------------
  687.  
  688. From ts@uwasa.fi Mon Jan 1 00:00:47 1996
  689. Subject: Does a file exist
  690.  
  691. 47. *****
  692.  Q: How can I test whether a file exists?
  693.  
  694.  A: There are several alternatives. Here is the most common with
  695. example code. It recognizes also read-only, hidden and system files.
  696.   function FILEXIST (name : string) : boolean;
  697.   var fm : byte;
  698.       f  : file;
  699.       b  : boolean;
  700.   begin
  701.     fm := FileMode;
  702.     FileMode := 0;
  703.     assign (f, name);
  704.     {$I-} reset(f); {$I+}
  705.     b := IOResult = 0;
  706.     if b then close(f);
  707.     filexist := b;
  708.     FileMode := fm;
  709.   end;
  710.  
  711. A second alternative is
  712.   Uses Dos;
  713.   function FILEXIST (name : string) : boolean;
  714.   var f  : file;
  715.       a  : word;
  716.   begin
  717.     assign (f, name);
  718.     GetFAttr (f, a);
  719.     filexist := false;
  720.     if DosError = 0 then
  721.       if ((a and Directory) = 0) and ((a and VolumeId) = 0) then
  722.         filexist := true;
  723.   end;
  724.  
  725. A third alternative is
  726.   Uses Dos;
  727.   function FILEXIST (name : PathStr) : boolean;
  728.   begin
  729.     filexist := FSearch (name, '') <> '';
  730.   end;
  731.  
  732. A fourth alternative is the following. Be careful with this option,
  733. since it works a bit differently from the others. It accepts wild
  734. cards. Thus, for example FILEXIST('c:\autoexec.*') would be TRUE in
  735. this method, while FALSE in all the above.
  736.   Uses Dos;
  737.   function FILEXIST (name : string) : boolean;
  738.   var f : SearchRec;
  739.   begin
  740.     filexist := false;
  741.     FindFirst (name, AnyFile, f);
  742.     if DosError = 0 then
  743.       if (f.attr <> Directory) and (f.attr <> VolumeId) then
  744.         filexist := true;
  745.   end;
  746. A good variation from KDT@newton.national-physical-lab.co.uk of this
  747. theme, disallowing wildcards:
  748.   function file_exists (fname :string) :boolean;
  749.   var f :searchrec;
  750.   begin
  751.     findfirst (fname, anyfile - directory - volumeid, f);
  752.     file_exists := (doserror + pos('*',fname) + pos('?',fname) = 0);
  753.   end;
  754. --------------------------------------------------------------------
  755.  
  756. From ts@uwasa.fi Mon Jan 1 00:00:48 1996
  757. Subject: The current program name
  758.  
  759. 48. *****
  760.  Q: What is the name of the current Turbo Pascal program?
  761.  
  762.  A: The name of the currently executing Turbo Pascal program is in
  763. ParamStr(0).
  764.    This was introduced in TP version 5.0, and as far as I recall at
  765. least MS-DOS version 3.0 is required. For TP 4.0 you can use
  766. "ParamStr0 The name of the program" from TSUNT45 in
  767. ftp://garbo.uwasa.fi/pc/ts/tspa3440.zip (or whatever the version
  768. number is the latest).
  769.    It is advisable to put the value into a string variable at be
  770. beginning of the program before eny I/O takes place. Thus you might
  771. wish to use:
  772.   var progname : string;
  773.   begin  { the main program }
  774.     progname := ParamStr(0);
  775.     :
  776. A bonus of this method is that you can access the individual
  777. characters of progname (e.g. progname[1] for the drive) while that
  778. is not possible to do for the ParamStr keyword.
  779. --------------------------------------------------------------------
  780.  
  781. From ts@uwasa.fi Mon Jan 1 00:00:49 1996
  782. Subject: How can a program reboot my PC?
  783.  
  784. 49. *****
  785.  Q: How is the code for rebooting the PC written in Turbo Pascal?
  786.  
  787.  A: This item draws from the information and the C-code example in
  788. Stan Brown's, later J.Carlyle's comp.os.msdos.programmer FAQ,
  789. ftp://garbo.uwasa.fi/pc/doc-net/dosfv204.zip (at the time of
  790. updating this), from memory.lst and interrup.b in
  791. ftp://garbo.uwasa.fi/pc/programming/inter48b.zip, and from
  792. ftp://garbo.uwasa.fi/pc/programming/helppc21.zip. The Turbo Pascal
  793. code is my adaptation of the C-code. It is not a one-to-one port.
  794.    The usually advocated warm-boot method is storing $1234 in the
  795. word at $0040:$0072 and jumping to address $FFFF:$0000. The problem
  796. with this approach is that files must first be closed, potential
  797. caches flushed. This is how to do this
  798.   procedure REBOOT;
  799.   label next;
  800.   var regs  : registers;
  801.       i     : byte;
  802.       ticks : longint;
  803.   begin
  804.     {... "press" alt-ctrl ...}
  805.     mem[$0040:$0017] := mem[$0040:$0017] or $0C;  { 00001100 }
  806.     {... "press" del, try a few times ...}
  807.     for i := 1 to 10 do
  808.       begin
  809.         FillChar (regs, sizeOf(regs), 0);  { initialize }
  810.         regs.ah := $4F;  { service number }
  811.         regs.al := $53;  { del key's scan code }
  812.         regs.flags := FCarry;  { "sentinel for ignoring key" }
  813.         Intr ($15, regs);
  814.         {... check if the del key registered, if not retry ...}
  815.         if regs.flags and Fcarry > 0 then goto next;
  816.         {... waste some time, watch out for midnight ...}
  817.         ticks := MemL [$0040:$006C];
  818.         repeat until (MemL[$0040:$006C] - ticks > 3) or
  819.                      (MemL[$0040:$006C] - ticks < 0)
  820.     end; {for}
  821.     exit;
  822.   next:
  823.     {... disk reset: writes all modified disk buffers to disk ...}
  824.     FillChar (regs, sizeOf(regs), 0);
  825.     regs.ah := $0D;
  826.     MsDos (regs);
  827.     {... set post-reset flag, use $0000 instead of $1234 for coldboot ...}
  828.     memW[$0040:$0072] := $1234;
  829.     {... jump to $FFFF:0000 BIOS reset ...}
  830.     Inline($EA/$00/$00/$FF/$FF);
  831.   end;  (* reboot *)
  832. One slight problem with this approach is that the keyboard intercept
  833. interrupt $15 service $4F requires at least an AT according to
  834. ftp://garbo.uwasa.fi/pc/programming/inter48b.zip. A simple test
  835. based on "FFFF:E byte ROM machine id" (the previous definition is
  836. from ftp://garbo.uwasa.fi/pc/programming/helppc21.zip) is:
  837.   function ISATFN : boolean;
  838.   begin
  839.      case Mem[$F000:$FFFE] of
  840.        $FC, $FA, $F8 : isatfn := true;
  841.        else isatfn := false;
  842.      end; {case}
  843.   end;  (* isatfn *)
  844. For a more comprehensive test use CPUFN "Get the type of the
  845. processor chip" from TSUNTH in ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip
  846. or see the TP + ASM code in Michael Ticher (1992), PC Intern System
  847. Programming, pp. 725-727.
  848.  
  849.    An addition by Per Bergland (d6caps@dtek.chalmers.se): I recently
  850. downloaded the FAQ for this newsgroup, and studied the code for
  851. rebooting a PC. The problem with that code (calling FFFF:0000) is
  852. that it will not work in protected mode programs such as those
  853. compiled for Windows or BP7 DPMI, or even in a DOS program run in a
  854. Windows DOS session. The solution provided has been tested on
  855. various COMPAQ PC:s, but I think it will work on any AT-class
  856. machine. It involves using the 8042 keyboard controller chip output
  857. pin 0, which is physically connected to the reset pin of the CPU.
  858. There is unfortunately no way to perform a "warm" reboot this way,
  859. and the warnings about disk caches etc apply to this code, too (see
  860. FAQ). The code is written in BP7 assembly lingo, because that's what
  861. I normally write code in, but anyone could rewrite it in C or high
  862. level Pascal.
  863.   UNIT Reboot;
  864.   INTERFACE
  865.     procedure DoReboot;
  866.   IMPLEMENTATION
  867.     procedure DoReboot;assembler;
  868.     asm
  869.       cli
  870.   @@WaitOutReady:       { Busy-wait until 8042 is ready for new command}
  871.       in al,64h         { read 8042 status byte}
  872.       test al,00000010b { Bit 1 of status indicates input buffer full }
  873.       jnz @@WaitOutReady
  874.       mov al,0FEh       { Pulse "reset" = 8042 pin 0 }
  875.       out 64h,al
  876.       { The PC will reboot now }
  877.     end;
  878.   END.
  879. --------------------------------------------------------------------
  880.  
  881. From ts@uwasa.fi Mon Jan 1 00:00:50 1996
  882. Subject: Writing inline code
  883.  
  884. 50. *****
  885.  Q: How can I write inline code?
  886.  
  887.  A: In Turbo Pascal versions prior 6.0 assembler code could not be
  888. directly included in the code. Instead one had to assemble the code
  889. into inline statements. Consider the task of rebooting the PC
  890. (without disk closing and cache flushing).  The assembler code for
  891. this is
  892.   mov ax,$40
  893.   mov ds,ax
  894.   mov wo [$72],$1234
  895.   jmp $FFFF:$0000
  896. To assemble this code into an inline statement write the following
  897. file calling it e.g. debug.in.  The empty line is important. Also
  898. carefully note that debug assumes hexadecimal notation. Do not use
  899. the $ designator in debug.in.
  900.   .... begin debug.in, cut here ....
  901.   a 100
  902.   mov ax,40
  903.   mov ds,ax
  904.   mov wo [72],1234
  905.   jmp FFFF:0000
  906.  
  907.   u 100
  908.   q
  909.   .... end debug.in, cut here ....
  910. Give the following command
  911.   debug < debug.in
  912. You'll get
  913.   0E9E:0100 B84000        MOV     AX,0040
  914.   0E9E:0103 8ED8          MOV     DS,AX
  915.   0E9E:0105 C70672003412  MOV     WORD PTR [0072],1234
  916.   0E9E:010B EA0000FFFF    JMP     FFFF:0000
  917. This translates into
  918.     Inline ($B8/$40/$00/
  919.             $8E/$D8/
  920.             $C7/$06/$72/$00/$34/$12/
  921.             $EA/$00/$00/$FF/$FF);
  922.  
  923.  A2: You can also utilize an inline <--> asm converter called
  924.   ftp://garbo.uwasa.fi/pub/pc/turbopas/inlin219.zip
  925.   inlin219.zip Inline assembler for Turbo Pascal, w/src, D.Baldwin
  926. It has two sources, inline.pas and uninline.pas which you can
  927. compile to do the conversions in both directions for you. For
  928. example, if you have a file test.asm containing the
  929.   mov ax,$0040
  930.   mov ds,ax
  931.   mov word ptr [$72],$1234
  932.   jmp far $FFFF:$0000
  933. then "inline test.asm" will produce test.obj with the following,
  934. expected contents
  935.   Inline(
  936.     $B8/$40/$00/           {mov ax,$0040}
  937.     $8E/$D8/               {mov ds,ax}
  938.     $C7/$06/$72/$00/$34/$12/ {mov word ptr [$72],$1234}
  939.     $EA/$00/$00/$FF/$FF);  {jmp far $FFFF:$0000}
  940. --------------------------------------------------------------------
  941.  
  942. From ts@uwasa.fi Mon Jan 1 00:00:51 1996
  943. Subject: Out of memory in compiling
  944.  
  945. 51. *****
  946.  Q: I am running out of memory when compiling my large program. What
  947.     can I do?
  948.  
  949.  A: If you are compiling your program from within the IDE (the
  950. Integrated Development Environment) then select the Option from the
  951. main menu, choose the Compiler item and set the Link buffer to
  952. Disk. (Also make the Compile option Destination to be Disk).
  953.    If this is not sufficient, next resort to using the TPC command
  954. line version of the Turbo Pascal compiler instead of the IDE.  Use
  955. the "Link buffer on disk" option.
  956.    Divide your program into units. It is advisable anyway for
  957. modularity when your program size grows.
  958.    If you have extended memory, instead of TURBO.EXE use TPX.EXE, if
  959. you have TP 7.0. If you are into protected mode programming then use
  960. Borland Pascal BP 7.0.
  961.  
  962.  A2: If you would prefer compiling your program from within the IDE
  963. but cannot do it for the above reason (or if you would prefer to
  964. compile your program from within your favorite editor instead of the
  965. TP IDE) you can use the following trick. If your editor has a macro
  966. language like most good editors do, the assign a hotkey macro that
  967. compiles the current file with the TPC. If you are using SemWare's
  968. QEdit editor you'll find such macros in ("Macros and configurations
  969. for QEdit text-editor") ftp://garbo.uwasa.fi/pc/ts/tsqed17.zip and
  970. in ftp://garbo.uwasa.fi/ts/tstse16.zip ("SAL macro sources to extend
  971. The SemWare Editor").
  972.    Also your editor must be swapped to disk during the compilation
  973. if memory is critical. There is a very good program for doing that:
  974. ftp://garbo.uwasa.fi/pc/sysutil/shrom24b.zip ("Shell Room, Swap to
  975. disk when shelling to application"). For example I invoke the QEdit
  976. editor with using the following batch:
  977.  c:\tools\shroom -s r:\cmand -z 1024 c:\qedit\q %1 %2 %3 %4 %5 %6 %7
  978. You'll find more about the switches in the Shell Room documentation.
  979. The -s switch designates the swap destination (my r:\cmand directory
  980. is on my ramdisk). The -z switch sets the shell environment size.
  981.   An unfortunate part is that the TP 5.0 Turbo Pascal IDE is about
  982. the only program I know that is not amenable the to Shell Room
  983. utility, so you cannot utilize Shell Room to swap the TP IDE to
  984. disk. Blessfully, at least TP 7.0 no more has this problem.
  985. --------------------------------------------------------------------
  986.  
  987. From ts@uwasa.fi Mon Jan 1 00:00:52 1996
  988. Subject: Last position write woes
  989.  
  990. 52. *****
  991.  Q: How do I avoid scrolling in the last column of the last row?
  992.  
  993.  A: If you use write or writeln at the last column of the last row
  994. (usually 80,25) the screen will scroll. If you wish to avoid the
  995. scrolling you'll have to use an alternative write that does not move
  996. the cursor. Here is a procedure to write without moving the cursor
  997.   uses Dos;
  998.   procedure WriteChar (Character : char; fgColor, bgColor : byte);
  999.   var r : registers;
  1000.   begin
  1001.     FillChar (r, SizeOf(r), 0);
  1002.     r.ah := $09;
  1003.     r.al := ord(Character);
  1004.     r.bl := (bgColor shl 4) or fgColor;
  1005.     r.cx := 1;    { Number of repeats }
  1006.     Intr ($10, r);
  1007.   end;  (* writechar *)
  1008. Thus, if you wish to write to the last column of the last row, you
  1009. must first move the cursor to that position. That can be done in
  1010. alternative ways. One might get there by having written previously
  1011. on the screen (with writeln and write routines) until one is in that
  1012. position. Another alternative is using GoToXY(80,20), but then you
  1013. have to use the Crt unit. If you don't want to use it, then you can
  1014. move the cursor by employing "GOATXY As the ordinary GoToXY but no
  1015. Crt unit required" from ftp://garbo.uwasa.fi/pc/ts/tspa3470.zip.
  1016.    There is an alternative interrupt service ($0A) which does the
  1017. same as service $09, but uses the default colors instead. Just
  1018. substitute $0A for $09, and leave the r.bl assignment out of the
  1019. WriteChar routine.
  1020.    Another option for writing anyhere on the screen without
  1021. affecting the cursor is using direct screen writes:
  1022.   uses Dos;
  1023.   procedure WriteChar (c : char; x, y : byte; fg, bg : byte);
  1024.   var vidstart : word;
  1025.       regs     : registers;
  1026.   begin
  1027.     FillChar (regs, SizeOf(regs), 0);
  1028.     regs.ah := $0F;
  1029.     Intr ($10, regs);  { Color or MonoChrome video adapter }
  1030.     if regs.al = 7 then vidstart := $B000 else vidstart := $B800;
  1031.     mem[vidstart:((y-1)*80+x-1)*2] := ord(c);
  1032.     mem[vidstart:((y-1)*80+x-1)*2+1] := (bg shl 4) or fg;
  1033.   end;
  1034. To write to the last position simply apply e.g.
  1035.   WriteChar ('X', 80, 25, 14, 0);  { Yellow on black }
  1036. The foreground (fg) and the background (bg) color codes are
  1037.   Black        =   0
  1038.   Blue         =   1
  1039.   Green        =   2
  1040.   Cyan         =   3
  1041.   Red          =   4
  1042.   Magenta      =   5
  1043.   Brown        =   6
  1044.   LightGray    =   7
  1045.   DarkGray     =   8
  1046.   LightBlue    =   9
  1047.   LightGreen   =  10
  1048.   LightCyan    =  11
  1049.   LightRed     =  12
  1050.   LightMagenta =  13
  1051.   Yellow       =  14
  1052.   White        =  15
  1053.   Blink        = 128
  1054. Yet another option is the following, but it needs the Crt unit. On
  1055. the other hand, it uses the default color. This is quite a good and
  1056. easy solution. I captured this fairly frequent idea from a posting
  1057. by Robert Buergi (nbuero@hslrswi.hasler.ascom.ch).
  1058.   uses Crt;
  1059.   procedure WriteToCorner (c : char);
  1060.   begin
  1061.     Inc (WindMax);
  1062.     GotoXY (80, 25);
  1063.     write (c);
  1064.     Dec (WindMax);
  1065.   end;  (* writeToCorner *)
  1066. --------------------------------------------------------------------
  1067.  
  1068. From ts@uwasa.fi Mon Jan 1 00:00:53 1996
  1069. Subject: Hiding a directory
  1070.  
  1071. 53. *****
  1072.  Q: How can one hide (or unhide) a directory using a TP program?
  1073.  
  1074.  A: Here is the code using interrupt programming. Incidentally,
  1075. since MS-DOS 5.0 the attrib command can be used to hide and unhide
  1076. directories.
  1077. (* Hide a directory. Before using it would be prudent to check
  1078.    that the directory exists, and that it is a directory.
  1079.    With a contribution from Jan Nielsen jak@hdc.hha.dk
  1080.    Based on information from Duncan (1986), p. 410 *)
  1081. procedure HIDE (dirname : string);
  1082. var regs : registers;
  1083. begin
  1084.   FillChar (regs, SizeOf(regs), 0);  { standard precaution }
  1085.   dirname := dirname + #0;           { requires ASCIIZ strings }
  1086.   regs.ah := $43;                    { function }
  1087.   regs.al := $01;                    { subfunction }
  1088.   regs.ds := Seg(dirname[1]);        { point to the name }
  1089.   regs.dx := Ofs(dirname[1]);
  1090.   regs.cx := 2; { set bit 1 on }     { to unhide set regs.cx := 0 }
  1091.   Intr ($21, regs);                  { call the interrupt }
  1092.   if regs.Flags and FCarry <> 0 then { were we successful }
  1093.     writeln ('Failed to hide');
  1094. end;  (* hide *)
  1095.  
  1096.  A2: An alternative method by Dr. Abimbola Olowofoyeku
  1097. laa12@seq1.keele.ac.uk. No paths.
  1098.   procedure HIDE (dirname : string);
  1099.   var FileInfo : searchRec;
  1100.       f        : file;
  1101.   begin
  1102.     FindFirst (dirname, Directory, FileInfo);
  1103.     while DosError = 0 do
  1104.       begin
  1105.         assign (f, FileInfo.Name);
  1106.         SetFAttr (f, Hidden);
  1107.         FindNext (FileInfo);
  1108.       end;
  1109.   end;  (* hide *)
  1110.   {}
  1111.   procedure UNHIDE (dirname : string);
  1112.   var FileInfo : searchRec;
  1113.       f        : file;
  1114.   begin
  1115.     FindFirst (dirname, AnyFile, FileInfo);
  1116.     while DosError = 0 do
  1117.       begin
  1118.         assign (f, FileInfo.Name);
  1119.         SetFAttr (f, Archive);
  1120.         FindNext (FileInfo);
  1121.       end;
  1122.   end;  (* unhide *)
  1123. --------------------------------------------------------------------
  1124.  
  1125. From ts@uwasa.fi Mon Jan 1 00:00:54 1996
  1126. Subject: Testing file opened status
  1127.  
  1128. 54. *****
  1129.  Q: How do I test whether a file is already open in a TP program?
  1130.  
  1131.  A: This question is best answered by providing the code:
  1132.   uses Dos;
  1133.   {... for non-text files ...}
  1134.   function ISFOPEN (var filePointer : file) : boolean;
  1135.   begin
  1136.     isfopen := FileRec(filePointer).mode <> FmClosed;
  1137.   end;
  1138.   {}
  1139.   {... for text files ...}
  1140.   function ISTOPEN (var filePointer : text) : boolean;
  1141.   begin
  1142.     istopen := TextRec(filePointer).mode <> FmClosed;
  1143.   end;
  1144.   {}
  1145.   procedure TEST;          { Testing a non-text file }
  1146.   const name = 'R:\TMP';
  1147.   var f  : file;
  1148.   begin
  1149.     Assign (f, name);
  1150.     writeln ('File ', name, ' is open is ', ISFOPEN(f));
  1151.     {$I-} rewrite (f); {$I+}
  1152.     if IOResult <> 0 then
  1153.       begin
  1154.         writeln ('Failed to open ', name);
  1155.         exit;
  1156.       end;
  1157.     writeln ('File ', name, ' is open is ', ISFOPEN(f));
  1158.     close(f);
  1159.     writeln ('File ', name, ' is open is ', ISFOPEN(f));
  1160.   end;
  1161. --------------------------------------------------------------------
  1162.  
  1163. From ts@uwasa.fi Mon Jan 1 00:00:55 1996
  1164. Subject: From string to real
  1165.  
  1166. 55. *****
  1167.  Q: How can I test and convert a numerical string into a real?
  1168.  
  1169.  A1: An easy task in Turbo Pascal but in standard Pascal this
  1170. frequent task is much trickier. Here are both the Turbo Pascal and
  1171. Standard Pascal versions for general edification :-).
  1172.   (* Convert and test a numerical string with Turbo Pascal *)
  1173.   function DIGVALFN (mj : string; var ok : boolean) : real;
  1174.   var k : integer;
  1175.       x : real;
  1176.   begin
  1177.     Val (mj, x, k);
  1178.     ok := k = 0;
  1179.     if ok then digvalfn := x else digvalfn := 0;
  1180.   end;  (* digvalfn *)
  1181.   {}
  1182.   (* Convert and test a numerical string with standard Pascal routines only *)
  1183.   procedure DIGVAL (mj : string; var number : real; var ok : boolean);
  1184.   label 1;
  1185.   var il, lenl, pl, kl1, kl2 : integer;
  1186.       nrol                   : boolean;
  1187.       numberdl               : real;
  1188.   begin
  1189.     ok := true; lenl := Length (mj); nrol := false; pl := 0; number := 0.0;
  1190.     if lenl = 0 then ok := false;
  1191.     for il:=2 to lenl do if (mj[il]='-') or (mj[il]='+') then ok := false;
  1192.     for il:=1 to lenl do
  1193.       case mj[il] of
  1194.         '0'..'9','+','-','.' : ; else ok := false;
  1195.       end;
  1196.     for il:=1 to lenl do
  1197.       case mj[il] of '0'..'9' : begin nrol := true; goto 1; end; end;
  1198.     1: if nrol = false then ok := false;
  1199.     for il:=1 to lenl do if mj[il] = '.' then pl := pl + 1;
  1200.     if pl > 1 then ok := false;
  1201.     kl1:=1; kl2:=lenl+1; if (mj[1]='-') or (mj[1]='+') then kl1 := 2;
  1202.     for il:=1 to lenl do if mj[il] = '.' then kl2 := il;
  1203.     if kl2-kl1 > 38 then ok := false;
  1204.     if ok then
  1205.       begin
  1206.         number:=0; numberdl:=0;
  1207.         for il:=kl1 to kl2-1 do number := (ord(mj[il])-48)+10*number;
  1208.         if kl2 < lenl+1 then
  1209.           for il:=lenl downto kl2+1 do
  1210.             numberdl := (ord(mj[il])-48)/10+numberdl/10;
  1211.         number := number + numberdl;
  1212.         if mj[1]='-' then number := -number;
  1213.       end; {if ok}
  1214.   end;  (* digval *)
  1215.   {}
  1216.   procedure TEST;
  1217.   var s : string; r : real; ok : boolean;
  1218.   begin
  1219.     s := '123.41';
  1220.     r := DIGVALFN (s, ok);
  1221.     if ok then writeln (r) else writeln ('Error in ', s);
  1222.     DIGVAL (s, r, ok);
  1223.     if ok then writeln (r) else writeln ('Error in ', s);
  1224.   end;
  1225.  
  1226. A2: The conversion can be in the other directorion as well. Here is
  1227. how to convert an integer into a string with standard Pascal
  1228. routines only.
  1229.   function CHRIVLFN (number : integer) : string;
  1230.   var il, pl, al : integer;
  1231.   cl, mj : string;
  1232.   isNeg : boolean;
  1233.   begin
  1234.     if number < 0 then begin
  1235.       isNeg := true; number := -number; end
  1236.       else isNeg := false;
  1237.     pl := 0; mj := ''; cl := '';
  1238.     repeat
  1239.       pl := pl + 1;
  1240.       al := number mod 10;
  1241.       cl := cl + chr(al+48);
  1242.       number := number div 10;
  1243.     until number = 0;
  1244.     if isNeg then begin pl := pl + 1; cl[pl] := '-'; end;
  1245.     for il := 1 to pl do mj := mj + cl[pl+1-il];
  1246.     chrivlfn := mj;
  1247.   end;  (* chrivlfn *)
  1248.   {}
  1249.   procedure TEST;
  1250.   var s : string; j : integer;
  1251.   begin
  1252.     j := 12341;
  1253.     s := CHRIVLFN (j);
  1254.     writeln (s);
  1255.   end;
  1256. --------------------------------------------------------------------
  1257.  
  1258. From ts@uwasa.fi Mon Jan 1 00:00:56 1996
  1259. Subject: Decompiling a TP .EXE
  1260.  
  1261. 56. *****
  1262.  Q: How can I reverse a TP .EXE or .TPU back into source code?
  1263.  
  1264.  A: This is simply asking too much. You cannot decompile a TP
  1265. program in a manner that would give you back the original source.
  1266. This method of reverse engineering is not on in actual practice.
  1267. Quoting Jeroen Pluimers (jeroenp@dragons.nest.nl) "During the
  1268. compilation, important information get's lost about variables,
  1269. types, identifiers etc. Writing a Pascal Decompiler is impossible.
  1270. The best you can achieve is a disassembler that can help you
  1271. recognize some Pascal statements."
  1272.    You might note that this question somewhat resembles another
  1273. frequent question "How can I convert a TPU unit of one TP version to
  1274. another?" which cannot be solved without the original source code.
  1275. --------------------------------------------------------------------
  1276.  
  1277. From ts@uwasa.fi Mon Jan 1 00:00:57 1996
  1278. Subject: Calculating date/time differences
  1279.  
  1280. 57. *****
  1281.  Q: How can I calculate the difference between two points of time?
  1282.  
  1283.  A: This is an unconfirmed answer so be a little careful with it.
  1284. But at the very least it shows some interesting information about
  1285. Turbo Pascal date/time conventions and how to declare and initialize
  1286. typed constants if they are records.
  1287.   program TimDifTest;
  1288.   uses Dos;
  1289.   const a : DateTime
  1290.           = (year:1992; month:10; day:24; hour:5; min:29; sec:38);
  1291.         b : DateTime
  1292.           = (year:1993; month:11; day:23; hour:6; min:30; sec:51);
  1293.   var aLong, bLong, cLong : longint;
  1294.       c : DateTime;
  1295.   begin
  1296.     PackTime (a, aLong);
  1297.     PackTime (b, bLong);
  1298.     cLong := bLong - aLong;
  1299.     UnpackTime (cLong, c);
  1300.     writeln (c.year-1980, ' ', c.month, ' ', c.day, ' ',
  1301.              c.hour, ' ', c.min, ' ', c.sec);
  1302.   end.
  1303. More generally than for dates between 1980 and 2079, or for more
  1304. accurate results, the difference between two date/times can be
  1305. calculated using Zeller's congruence (see the item "I want code that
  1306. gives the weekday of the given date"). First calculate Zeller's for
  1307. both the dates, convert them, and the hour, min, and sec into
  1308. seconds, subtract, and convert back.
  1309. --------------------------------------------------------------------
  1310.  
  1311. From ts@uwasa.fi Mon Jan 1 00:00:58 1996
  1312. Subject: Stand-alone or from IDE
  1313.  
  1314. 58. *****
  1315.  Q: Is a program running stand-alone or from within the IDE?
  1316.  
  1317.  A: Not all questions have an answer yet. I posed this question to
  1318. the the late UseNet newsgroup comp.lang.pascal, but we have not
  1319. found an answer that would be general for all MS-DOS versions. The
  1320. closest we have comes from dmurdoch@mast.queensu.ca Duncan Murdoch
  1321. (naturally :-). I have done some slight editing of Duncan's
  1322. solution.
  1323.   uses Dos;
  1324.   type Pchar = ^Char;
  1325.   function Asciiz2Str (p : Pchar) : string;
  1326.   var
  1327.     result : string;
  1328.     len : byte;
  1329.   begin
  1330.     len := 0;
  1331.     while (p^ <> #0) and (len < 255) do
  1332.     begin
  1333.       inc(len);
  1334.       result[len] := p^;
  1335.       inc(longint(p));
  1336.     end;
  1337.     result[0] := chr(len);
  1338.     Asciiz2Str := result;
  1339.   end;
  1340.   {}
  1341.   var parentSeg : ^word;
  1342.       p         : pchar;
  1343.   begin
  1344.     if swap(DosVersion) < $0400 then
  1345.       writeln ('Requires Dos 4.0+')
  1346.     else begin
  1347.       parentSeg := ptr (prefixSeg, $16);
  1348.       p := ptr (ParentSeg^-1, 8);
  1349.       writeln ('I was launched by ', Asciiz2Str(p));
  1350.     end;
  1351.   end.
  1352. Another suggestion has been that the contents of ParamStr(0) would
  1353. show the launching program. I tested this on several configurations
  1354. and TP versions and found no evidence that the contention would
  1355. hold.
  1356. --------------------------------------------------------------------
  1357.  
  1358. From ts@uwasa.fi Mon Jan 1 00:00:59 1996
  1359. Subject: Memory Addressing
  1360.  
  1361. 59. *****
  1362.  Q: Please explain Turbo Pascal memory addressing to me.
  1363.  
  1364.  A: This is far from an easy question, but let's see what we can do.
  1365. The origins of the difficulties are in the design of the 8086 chip
  1366. which still restricts all Turbo Pascal applications (which contrary
  1367. to Borland Pascal use the original real mode). The 8086 (aka real
  1368. mode) addressing is based on 16-bit registers. As you probably know
  1369. 2^16 is 65536 which means that you cannot directly point to all
  1370. addresses of the lower and upper memory, which ranges from 0 to
  1371. 1048575 (2^20-1). Thus all the memory addresses are pointed to into
  1372. two parts in TP programs, the segment and the offset. The following
  1373. example of the PC's memory illustrates.
  1374.  
  1375.   Decimal  Hexa-
  1376.   address  decimal  Segment  Offset  What
  1377.         0   $00000    $0000   $0000  Conventional memory starts
  1378.      1043   $00413    $0040   $0013  Base memory in Kb, a word
  1379.    655359   $9FFFF    $9000   $FFFF  Conventional memory ends
  1380.    655360   $A0000    $A000   $0000  Upper memory begins
  1381.   1048575   $FFFFF    $F000   $FFFF  Upper memory ends
  1382.  
  1383. To exemplify, let's look at some alternative ways we could access
  1384. the information about the amount of the base memory. It is very
  1385. straight-forward, since in a PC that information is at the fixed
  1386. memory location show by the above table. We know this in advance.
  1387. Using direct memory accessing we could write
  1388.   var memsize : word;
  1389.   memsize := MemW [$0040:$0013];
  1390.   writeln (memsize);
  1391.   {.. or ..}
  1392.   var memsize : word absolute $0040:$0013;
  1393.   writeln (memsize);
  1394. If you are not familiar with the true meaning of pointers, they may
  1395. feel confusing, but what they basically are is just what the name
  1396. indicates, pointers to memory locations. Study the following
  1397. example.
  1398.   var memsizePtr : ^word;           { A pointer to a word }
  1399.   begin
  1400.     memsizePtr := ptr ($40, $13);   { Assign the pointer a value }
  1401.     writeln (memsizePtr^);          { Write what is in the address }
  1402.   end.                              { that was pointed to }
  1403. This was relatively simple, since we knew in advance the location of
  1404. the information. Lets look at a case where we do not know that.
  1405. Consider
  1406.   var x : word;
  1407.   begin
  1408.     x := 1223;
  1409.     writeln (x);
  1410.   end.
  1411. We have a variable x somewhere in the memory, and we can refer to it
  1412. without ever needing to know where the variable actually is in the
  1413. memory. But how does one find out if one for some reason wants or
  1414. needs to know?
  1415.   var x       : word;
  1416.       Segment : word;
  1417.       Offset  : word;
  1418.       y       : ^word;
  1419.   begin
  1420.     x := 1223;
  1421.     writeln (x);
  1422.     Segment := Seg(x);
  1423.     Offset  := Ofs(x);
  1424.     writeln ('Variable x is at $', HEXFN(Segment), ':$', HEXFN(Offset));
  1425.     {... one test to ensure that the value really is in there ...}
  1426.     writeln (MemW [Segment:Offset]);
  1427.     {... another test to demonstrate that the value really is in there ...}
  1428.     y := Addr(x);
  1429.     writeln (y^);
  1430.   end.
  1431. Next consider
  1432.   var xPtr    : ^word;
  1433.       Segment : word;
  1434.       Offset  : word;
  1435.       yPtr    : ^word;
  1436.   begin
  1437.     xPtr^ := 1223;
  1438.     writeln (xPtr^);
  1439.     Segment := Seg(xPtr^);
  1440.     Offset  := Ofs(xPtr^);
  1441.     writeln ('$', HEXFN(Segment), ':$', HEXFN(Offset));
  1442.     {... a test to ensure that the value really is in there ...}
  1443.     yPtr := Ptr (Segment, Offset);
  1444.     writeln (yPtr^);
  1445.   end.
  1446. A further aspect of pointers is that you can utilize them to put a
  1447. variables onto the heap instead of the data segment so that you
  1448. won't run so easily out of space.
  1449.   var xPtr : ^word;
  1450.   begin
  1451.     { Put it onto the heap }
  1452.     New (xPtr);
  1453.     xPtr^ := 1223;
  1454.     writeln (xPtr^);
  1455.     { Get rid of it }
  1456.     Dispose (xPtr); xPtr := nil;
  1457.     readln;
  1458.   end.
  1459. Let us return to the addressing. The formulas for converting between
  1460. the addresses (sent in by Duncan Murdoch) are
  1461.   Physical := longint(segment)*16 + offset;
  1462.   {}
  1463.   Segment  := Physical div 16;
  1464.   Offset   := Physical mod 16; { This gives the normalized form }
  1465. There are multiple Segment:Offset pairs that refer to the same
  1466. address, e.g. $0000:$0413 and $0040:$0013. The normalized addresses,
  1467. with the offset in the range of 0 to $F, are, however, unique. An
  1468. example $0041:$0003.
  1469. --------------------------------------------------------------------
  1470.  
  1471. From ts@uwasa.fi Mon Jan 1 00:00:60 1996
  1472. Subject: Getting a bit from a byte
  1473.  
  1474. 60. *****
  1475.  Q: How do I obtain a bit or bits from a byte, a word or a longint?
  1476.  
  1477.  A: For bit operations think of the variable as a binary number
  1478. instead of a decimal. Consider for example
  1479.   var x : word;
  1480.   x := 219;
  1481. In binary presentation it is
  1482.   The word                  0000 0000 1101 1011
  1483.   Position in the word      FEDC BA98 7654 3210
  1484.  
  1485. Say you need the value of bit 6 (the seventh bit) in the word. You
  1486. can "and" the following words
  1487.   0000 0000 1101 1011    (219)
  1488.   0000 0000 0100 0000    ( 64)
  1489. In decimal TP notation this amounts to
  1490.   var b : word;
  1491.   b := x and 64;
  1492. The value of b is now
  1493.   0000 0000 0100 0000    ( 64)
  1494. To get the bit value (0 or 1) you need to shift the result right by
  1495. six steps, that this the expression becomes the often seen but
  1496. cryptic
  1497.   b := (x and 64) shr 6;
  1498. which means that the value of b is finally 1 in this example.
  1499.  
  1500. Ok, but what then if you need the combined value of bits six and
  1501. seven. The answer is evident if you consider the binary presentation
  1502.   0000 0000 1101 1011    (219)
  1503.   0000 0000 1100 0000    (192)
  1504. hence
  1505.   b := (x and 192) shr 6;
  1506. which will give 3 as it should.
  1507.  
  1508. So far, so good. What if you need to turn on bit nine in a word
  1509. without interfering with the other bits. The binary presentation,
  1510. again, is the key. You'll have to "or" the following
  1511.   0000 0000 1101 1011    (219)
  1512.   0000 0010 0000 0000    (512)
  1513. that is
  1514.   x := x or 512;
  1515. This results to
  1516.   0000 0010 1101 1011    (731)
  1517.  
  1518. What if you wish to turn off, say bit 6, in
  1519.   0000 0000 1101 1011    (219)
  1520.   1111 1111 1011 1111  (65471)
  1521. This is achieved by
  1522.   x := 219;
  1523.   x := x and 65471;
  1524. This results to
  1525.   0000 0000 1001 1011    (155)
  1526.  
  1527. Consider the following application as an example. The number of a
  1528. PC's floppy disk drives (minus one) is stored in bits 6 and 7 in a
  1529. word returned by interrupt $11. This is the code to find out how
  1530. many disk drives a PC has.
  1531.   uses Dos;
  1532.   function NrOfFDiskDrives : byte;
  1533.   var regs : registers;
  1534.   begin
  1535.     Intr ($11, regs);
  1536.     NrOfFDiskDrives := ((regs.ax and 192) shr 6) + 1;
  1537.   end;
  1538.  
  1539. A tip from Duncan Murdoch.  You might wish to predefine the
  1540. following constants for easier handling
  1541.   const bit0  = 1;
  1542.         bit1  = 2;
  1543.         bit2  = 4;
  1544.         :
  1545.         bit15 = 32768;
  1546.         :
  1547.         bit31 = 2147483648;
  1548. Or to put it slightly differently as Dr John Stockton
  1549. jrs@dclf.npl.co.uk suggests
  1550.   const
  1551.   bit00=$00000001; bit01=$00000002; bit02=$00000004; bit03=$00000008;
  1552.   bit04=$00000010; bit05=$00000020; bit06=$00000040; bit07=$00000080;
  1553.   :
  1554.   bit28=$10000000; bit29=$20000000; bit30=$40000000; bit31=$80000000;
  1555. Finally, you also might want to look at the item "Getting a nybble
  1556. from a byte".
  1557. --------------------------------------------------------------------
  1558.